#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h> 
#include <time.h>
#include <sys/wait.h> 
#include <unistd.h>
#include <stdarg.h>
#include <sys/time.h>


//下面都是本地文件
#include "../head/default.h"         //define 宏定义及static变量                                      
#include "../head/log.h"         //define 宏定义及static变量 

void f_logging(enum LogLevel level, const char *fmt, ...);
int f_my_strncpy(char *string, int n);
int f_default_check_comm(char *bl_comm[], int max_index, char comm[]);
int f_get_md5(char str[], char res_md5[]);
int f_find_char (char str[], char *value);
int f_my_split(char buf[], const char *split, char *res[]);
int f_my_check_ip(char *argv[], int role, int max_index, char addr[]);
int f_get_log_file(char path[], char last_name[]);
int f_check_comm_on_server(char *argv[], char comm[]);
int f_my_system(const char *str, const char md5[], char result[]);
int f_replace_string(char * source_str,char * targ_str,char *val);

int f_server(char *argv[], int daemon) {

    char buffer[VAR_MAX_LEN], log_info[VAR_MAX_LEN], res[RES_MAX_LEN];
    int sock_fd, new_fd;
    struct sockaddr_in server_addr, client_addr;
    socklen_t sin_size;
    ssize_t nbytes;
    int on = 1;

    if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        f_logging(ERROR, "SERVER --> The socket abnormal!");
        return -1;
    }

    setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

    memset(&server_addr, 0, sizeof(struct sockaddr_in));
    server_addr.sin_family      = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port        = htons(atoi(argv[2]));

    if (bind(sock_fd, (struct sockaddr *)(&server_addr), sizeof(struct sockaddr)) == -1) {
        if (daemon == 0) { //daemon = 0表示不是守护进程在监测端口是否被占用, 所以需要打印日志, 反之是守护进程在检测端口是否被占用, 则不打印日志
            f_logging(ERROR, "SERVER --> The bind abnormal!");
        }
        return -1;
    }

    if (daemon == 1) { //daemon = 1表示守护进程在监测端口, 执行到这里表示端口没有被占用, 所以会返回然后重新调用f_server函数进行监听
        close(sock_fd);
        return 0;
    }

    if (listen(sock_fd, BACKLOG) == -1) {
        f_logging(ERROR, "SERVER --> The listen abnormal!");
        return -1;
    }

    sprintf(log_info, "%s%s", "SERVER --> Server started. Listen port : ", argv[2]);
    f_logging(INFO, log_info);
    memset(log_info, 0x00, sizeof(log_info));
    int mark = 1;   //是否在白名单里面, 是否在黑名单里面
    char tmp_argv[VAR_MAX_LEN];
    char *split = ",";
    while (1) {

        sin_size = sizeof(struct sockaddr_in);
        if ((new_fd = accept(sock_fd, (struct sockaddr *)(&client_addr), &sin_size)) == -1) {
            f_logging(ERROR, "SERVER --> The accept abnormal!");
            return -1;
        }

        if (strcmp(inet_ntoa(client_addr.sin_addr), "127.0.0.1") == 0) { //表示是守护进程在监测端口是否正常
            close(new_fd);
            continue;
        }

        if (strlen(argv[4]) != 0) {
            strcpy(tmp_argv, argv[4]);
            int max_index = f_find_char(tmp_argv, split) + 1;
            char *res[20];
            for(int i = 0; i < max_index; i++) { //定义一个临时变量, 用来接收读取配置文件的字符串
                res[i] = (char *)malloc(20);
            }
            f_my_split(tmp_argv, split, res);
            mark = f_my_check_ip(res, 1, max_index, inet_ntoa(client_addr.sin_addr));
            for(int i = 0; i < max_index; i++) { //定义一个临时变量, 用来接收读取配置文件的字符串
                free(res[i]);
            }
            
        } else {
            mark = 0;
        }

        if (mark == 1) {
            memset(log_info, 0x00, sizeof(log_info));
            sprintf(log_info,"%s%s%s",
                    "SERVER --> The server uses a host-white-list and the address[", inet_ntoa(client_addr.sin_addr),"] is not in the whitelist");
            f_logging(ERROR, log_info);
            strcpy(res, "CLIENT --> Access denied.");
            write(new_fd, res, strlen(res)); //按照1024一段一段写, 节省内存开销
            close(new_fd);
            memset(res, 0x00, sizeof(res));
            continue;
        }

        if (strlen(argv[5]) != 0) {
            memset(tmp_argv, 0x00, sizeof(tmp_argv));
            strcpy(tmp_argv, argv[5]);
            int max_index = f_find_char(tmp_argv, split) + 1;
            char *res[20];  //ip最长是18
            for(int i = 0; i < max_index; i++) { //定义一个临时变量, 用来接收读取配置文件的字符串
                res[i] = (char *)malloc(20);
            }
            f_my_split(tmp_argv, split, res);
            mark = f_my_check_ip(res, 0, max_index, inet_ntoa(client_addr.sin_addr));
            for(int i = 0; i < max_index; i++) { //定义一个临时变量, 用来接收读取配置文件的字符串
                free(res[i]);
            }
        } else {
            mark = 0;
        }

        if (mark == 1) { //如果在黑名单里面, 表示不能访问, 直接拒绝了
            strcpy(res, "CLIENT --> Access denied.");
            write(new_fd, res, strlen(res)); //按照1024一段一段写, 节省内存开销
            close(new_fd);
            memset(res, 0x00, sizeof(res));
            continue;
        }

        if ((nbytes = read(new_fd, buffer, VAR_MAX_LEN)) == -1) {
            f_logging(ERROR, "SERVER --> Failed to read message!");
            return -1;
        }

        buffer[nbytes] = '\0';
        char check[4];
        f_get_log_file(buffer, check); //返回/后面的字符串, 其实就是定义的协议, 返回部分表示这个串总共多长
        if (strlen(check) == 0) { //check为空表示接收到的信息有丢失
            strcpy(check, "0");
        }


        f_my_strncpy(buffer, (int)strlen(buffer) - (int)strlen(check) - 1);

        //验证接收到的字符串是否和client传过来的是一样的, 主要是通过判断字符串的长度
        if (strlen(buffer) != atoi(check)) { //验证字符串是否完整
            memset(log_info, 0x00, sizeof(log_info));
            sprintf(log_info, "%s%s]", "SERVER --> Failed to read message! This information is incomplete! [", buffer);
            f_logging(ERROR, log_info);
            close(new_fd);
            memset(res, 0x00, sizeof(res));
            continue;
        }

        char pass[VAR_MAX_LEN];
        f_get_log_file(buffer, pass);    //返回client 传过来的pass
        f_my_strncpy(buffer, (int)strlen(buffer) - (int)strlen(pass) - 1);
        
        //验证密码是否一样
        if ((strcmp(pass, argv[6]) != 0)) {
            memset(log_info, 0x00, sizeof(log_info));
            sprintf(log_info, "%s%s%s", "SERVER --> The client [ ", inet_ntoa(client_addr.sin_addr), " ] password is incorrect!");
            f_logging(ERROR, log_info);
            memset(res, 0x00, sizeof(res));
            strcpy(res, "CLIENT --> Access denied.");
            write(new_fd, res, strlen(res)); //按照1024一段一段写, 节省内存开销
            close(new_fd);
            memset(res, 0x00, sizeof(res));
            continue;
        }


        char client_md5[PASS_MAX_LEN];
        f_get_log_file(buffer, client_md5); //获取client生成的md5
        f_my_strncpy(buffer, (int)strlen(buffer) - (int)strlen(client_md5) - 1);
        memset(log_info, 0x00, sizeof(log_info));
        sprintf(log_info, "%s%s", "SERVER --> Server get connection from ", inet_ntoa(client_addr.sin_addr));
        f_logging(INFO, log_info);

        //验证命令是否合法
        if (strlen(argv[9]) != 0) {
            int mark = f_check_comm_on_server(argv, buffer);
            if (mark != 0) {
                strcpy(res, "CLIENT --> This command is not supported!.");
                write(new_fd, res, strlen(res)); //按照1024一段一段写, 节省内存开销
                close(new_fd);
                memset(res, 0x00, sizeof(res));
                continue;
            }
        }

        int cmd_state = f_my_system(buffer, client_md5, res);

        if (cmd_state == 0) { //返回一个json串, 保存状态码及错误信息
            char _res_tmp[RES_MAX_LEN];
            f_replace_string(res, "\"", "'");
            strcpy(_res_tmp, res);
            memset(res, 0x00, sizeof(res));
            sprintf(res,"%s\n{\"STATE\":%d,\"INFO\":\"OK\"} ",_res_tmp , cmd_state);
            memset(_res_tmp, 0x00, sizeof(_res_tmp));
        } else {
            char _res_tmp[RES_MAX_LEN];
            f_replace_string(res, "\"", "'");
            strcpy(_res_tmp, res);
            memset(res, 0x00, sizeof(res));
            sprintf(res,"{\"STATE\":%d,\"INFO\":\"%s\"} ",cmd_state, _res_tmp);
            memset(_res_tmp, 0x00, sizeof(_res_tmp));
        }
        f_my_strncpy(res, (int)strlen(res) - 1);
        //write(new_fd, res, strlen(res));
        ssize_t nbytes = 0; //定义一个字符位移量
        while (nbytes <= strlen(res)) {  //将命令输出结果写道accept fd, 相当于返回给client
            write(new_fd, res + nbytes, VAR_MAX_LEN); //按照1024一段一段写, 节省内存开销
            //send(new_fd, res + nbytes, VAR_MAX_LEN, 0);
            nbytes += VAR_MAX_LEN;    //移动位移
        }

        memset(log_info, 0x00, sizeof(log_info));
        if (cmd_state == -1) {
            sprintf(log_info, "%s%s%s%s%s", "SERVER --> [ cmd : ", buffer, " ] [ CLIENT : " , inet_ntoa(client_addr.sin_addr)," ]");
            f_logging(ERROR, log_info);
        } else if (cmd_state == -2) {
            sprintf(log_info, "%s%s%s%s%s", "SERVER --> [ cmd : ", buffer, " ] [ CLIENT : " , inet_ntoa(client_addr.sin_addr),"] [ Popen error! ]");
            f_logging(ERROR, log_info);
        } else {
            sprintf(log_info, "%s%s%s%s%s", "SERVER --> [ cmd : ", buffer, " ] [ CLIENT : " , inet_ntoa(client_addr.sin_addr),"] [ Command executed successfully! ]");
            f_logging(INFO, log_info);
        }

        memset(buffer, 0x00, sizeof(buffer));
        memset(res, 0x00, sizeof(res));
        sprintf(log_info, "%s%s", "SERVER --> Disconnect from ", inet_ntoa(client_addr.sin_addr));
        f_logging(INFO, log_info);
        close(new_fd);
    }
    return 0;
}
